#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/sched.h>

#define BUF_NUM 5 /* the size of buffer area */
#define N 5 /* the number of consumers(or producers) */
#define PNUM 3 /* the number of a producer producing the goods */
#define CNUM N*PNUM /* the sum of all consumers consuming the goods */

static int index[N];/* the index of consumers or producers */
struct semaphore empty;
struct semaphore full;
struct semaphore mutex;
static int buf[BUF_NUM];/* the buffer area */
static int in,out;/* the index of buffer area */
static int cflag;/* the flag of marking the sum of consuming*/

int producer_thread(void *p)
{
	int i = *(int *)p;
	int p_num = PNUM;

	/*a producer can produce only PNUM goods*/
	while(p_num)
	{
		down(&empty);
	        down(&mutex);

		/* critical section */

          	buf[in] = i * 100 + (PNUM - p_num + 1);/* produce a new goods */
          	printk("[producer %d]:I producted a goods \"%d\" to buf[%d]..\n",i,buf[in],in);
            	in = (in + 1) % BUF_NUM;
         	
		up(&mutex);
          	up(&full);
		p_num--;
	}
	return 0;
}

int consumer_thread(void *p)
{
	int i = *(int *)p;
	int goods;

	while(CNUM != cflag)
	{
	        down(&full);
            	down(&mutex);
          	
		/* critical section */
		
		goods = buf[out];/* take a goods */
           	printk("[consumer %d]:I consumed a goods \"%d\" from buf[%d]..\n",i,goods,(out%BUF_NUM));
           	out = (out + 1) % BUF_NUM;

            	up(&mutex);
            	up(&empty);
		cflag++;
	}
	return 0;
	
}

static int __init npncnb_init(void)
{
	int i;

	printk("np_nc_nb module is working..\n");
	
	in = out = 0;
	cflag = 0;
	/* initialize the semaphore */
	init_MUTEX(&mutex);
	sema_init(&empty,BUF_NUM);
	sema_init(&full,0);

	/* create N producers and consumers  */
	for(i = 0; i < N; i++)
	{
		index[i] = i+1;
		kernel_thread(producer_thread, &(index[i]), CLONE_KERNEL);
		kernel_thread(consumer_thread, &(index[i]), CLONE_KERNEL);
	}

	return 0;
}

static void __exit npncnb_exit(void)
{
	printk("np_nc_nb module is leaving..\n");
}

module_init(npncnb_init);
module_exit(npncnb_exit);
MODULE_LICENSE("GPL");
